package aiaudio.collaborative;

import aiaudio.storage.api.IArtist;
import aiaudio.storage.api.IAudioTestSet;
import aiaudio.storage.api.IAudioTrainingSet;
import aiaudio.storage.api.IUser;
import aiaudio.storage.api.InternalException;
import aiaudio.storage.api.RevereUserArtistTuple;
import aiaudio.storage.api.Tuple;
import aiaudio.storage.api.UserArtistTuple;
import aiaudio.storage.simple.SimpleIncludeAudioTrainingStorage;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

/**
 *
 * @author Nastya
 */
public class CollaborativeAudioTrainingManager {

    private SimpleIncludeAudioTrainingStorage storage;
    private HashMap<RevereUserArtistTuple, Tuple<Double, Double>> resultRatings;
    private int trainingCiclesCount;

    public CollaborativeAudioTrainingManager(
            SimpleIncludeAudioTrainingStorage storage,
            int trainingCiclesCount) {

        this.storage = storage;
        this.trainingCiclesCount = trainingCiclesCount;
    }

    public void Run() throws InternalException {
        IAudioTrainingSet firstTrainingSet = storage.getTrainingSet(0);
        IAudioTrainingSet secondTrainingSet = storage.getTrainingSet(1);
        IAudioTestSet testSet = storage.getTestSet();

        CollaborativeAudioTrainingNetwork network =
                new CollaborativeAudioTrainingNetwork(firstTrainingSet,
                trainingCiclesCount);
        network.process();

        CollaborativeGroupping groupping = network.getGroupping();

        Iterator<UserArtistTuple> tuplesForTest =
                testSet.getAvaliableTuples();

        ResultTestHolder calculatedAndActualRatings =
                new ResultTestHolder();

        while (tuplesForTest.hasNext()) {
            calculateNewUserArtistRating(tuplesForTest, groupping,
                    secondTrainingSet, testSet, calculatedAndActualRatings);
        }

        resultRatings = calculatedAndActualRatings;
    }

    private void calculateNewUserArtistRating(
            Iterator<UserArtistTuple> tuplesForTest,
            CollaborativeGroupping groupping,
            IAudioTrainingSet secondTrainingSet,
            IAudioTestSet testSet,
            ResultTestHolder calculatedAndActualRatings) throws InternalException {
        UserArtistTuple userArtistTuple = tuplesForTest.next();
        IArtist artist = userArtistTuple.getSecond();
        IUser user = userArtistTuple.getFirst();

        double calculatedRating = weightedAverageRaitingForUser(groupping,
                userArtistTuple, secondTrainingSet);
        double actualRating = testSet.getRatings(artist, user);

        Tuple<Double, Double> resultTuple =
                new Tuple<Double, Double>(calculatedRating, actualRating);
        calculatedAndActualRatings.put(userArtistTuple.cloneReverse(),
                resultTuple);
    }

    private double weightedAverageRaitingForUser(CollaborativeGroupping groupping,
            UserArtistTuple tuple,
            IAudioTrainingSet trainingSet) throws InternalException {

        List<CollaborativeUserGroup> userGroups =
                groupping.getMembersByUser(tuple.getFirst());

        double sumaryRating = 0;
        double summaryTrustingWeight = 0;

        for (CollaborativeUserGroup userGroup : userGroups) {
            IArtist artist = tuple.getSecond();
            int groupId = userGroup.getGroupId();

            double groupAverageWeightingRaiting =
                    weightedAverageRaitingInGroup(groupping, groupId,
                    trainingSet, artist);

            sumaryRating += groupAverageWeightingRaiting;
            summaryTrustingWeight += userGroup.getFitness();
        }

        return sumaryRating / summaryTrustingWeight;
    }

    private double weightedAverageRaitingInGroup(CollaborativeGroupping groupping,
            int groupId, IAudioTrainingSet trainingSet,
            IArtist artist) throws InternalException {

        List<CollaborativeUserGroup> usersInGroup =
                groupping.getMembersByGroupId(groupId);

        double summaryRating = 0;
        double summaryWeight = 0;
        for (CollaborativeUserGroup userGroup : usersInGroup) {
            IUser user = userGroup.getUser();
            double trainedRating = trainingSet.getTrainingRating(artist, user);

            if (trainedRating == -1) {
                continue;
            }

            summaryRating += trainedRating * userGroup.getFitness();
            summaryWeight += userGroup.getFitness();
        }

        return summaryRating / summaryWeight;
    }

    public HashMap<RevereUserArtistTuple, Tuple<Double, Double>> getResultRatings() {
        return resultRatings;
    }
}